
//文件上传的状态值
export enum statusCode {
	//待上传
	upload = 0,
	//上传中
	uploading = 1,
	//上传失败
	fail = 2,
	//上传成功
	success = 3,
	//超过大小限制
	max = 4,
}
//文件对象结构
export interface file {
	url:string,//当前显示的图片地址，这是个本地临时地址。待上传前，成功后会替换服务器地址。
	status?:string,//上传状态文本
	progress?:number,//当前文件上传的进度
	uid?:string|number,//文件唯一标识id
	statusCode?:statusCode,//文件状态
	response?:any,//上传成功后的回调数据。
	name?:string ,//文件名称
	[propName: string]: any
}
//上传class对象的配置。
export interface fileConfig {
	maxSize?:number,//每一个文件上传的最大尺寸，默认为10mb
	maxFile?:number,//一次选择文件最大数量。
	fileType?:Array<string>,//文件选择的类型。
	hostUrl?:string,//上传文件的服务器地址
	fileList?:Array<file>,//已上传的文件列表。
	autoUpload?:Boolean,
	header?:Object,//头部参数。
	formData?:Object,//额外的表单数据。
	formName?:string

}
export function getUid (length=3){
	return Number(Number(Math.random().toString().substr(3,length) + Date.now()).toString(8));
}
/**
 * 上传文件。
 * 作者：tmzdy
 * 时间：2022年4月27日
 * 联系：zhongjihan@sina.com
 * @method {Function} beforeChooesefile -- 选择图片上传前执行的勾子。
 * @method {Function} chooesefile -- 选择图片上传，弹出层进行选择文件 。
 * @method {Function} chooesefileAfter -- 选择图片、视频文件成功后触发。返回选择后的文件。此时还未加入待上传列表，会返回 一个文件列表用于过滤。需要在函数中再返回 一个文件 列表，
 * @method {Function} chooesefileSuccess -- 文件已经加入到了待上传文件列表,需要返回过滤的文件列表
 * @method {Function} beforeAddfile -- 动态加入预计加入文件前执行的勾子，返回true才会正式加入到待上传列表中。
 * @method {Function} addFile -- 动态加入预上传的文件。
 * @method {Function} progress -- 进度。
 * @method {Function} fail -- 失败。服务器出现非200时触发。
 * @method {Function} beforeSuccess -- 服务器返回成功，立即执行的勾子。如果此时返回false,则表示文件 上传失败。
 * @method {Function} success -- 成功。
 * @method {Function} complete -- 单个文件完成。不管上传成功与失败否，都会触发此函数。
 * @method {Function} uploadComplete -- 所有文件上传完时触发
 * @method {Function} beforeStart -- 开始上传。前的校验勾子。如果返回false,将阻止上传
 * @method {Function} start -- 开始上传。
 * @method {Function} stop -- 停止上传。中止上传时触发的函数。
 */
export class uploadfile {
	//文件列表。
	filelist:Array<file> = [];
	isStop = false;
	index = 0;
	config:fileConfig = {};
	uploadobj:UniNamespace.UploadTask|null = null;
	constructor(config:fileConfig) {
		let cf:fileConfig =  {maxSize:10*1024*1024,maxFile:9,fileType:['album','camera'],fileList:[],autoUpload:true,header:{},formData:{},formName:'file'}
		cf = {...cf,...arguments[0]??{}};
		//配置{name: 'file', // 上传时的文件key名。默认file,header: {}, // 上传的头部参数。}
	    this.config=cf;
		this.addFile(cf.fileList);
		delete this.config.fileList;
	}
	async beforeChooesefile(){
		return true;
	}
	async chooesefileAfter(fileList:Array<file>){
		
		return fileList;
	}
	async chooesefileSuccess(fileList:Array<file>){
		
		return fileList;
	}
	delete(item:file){
		let index = this.filelist.findIndex(el=>el.uid == item.uid);
		if(index>-1){
			let p = [...this.filelist]
			p.splice(index,1)
			this.filelist = [...p];
		}
		 
		return this.filelist;
	}
	async clear(){
		/** 清清前要选暂停所有正在上传的文件 */
		this.stop();
		this.filelist = [];
	}
	setFileStatus(item:file){
		let index = this.filelist.findIndex(el=>el.uid == item.uid);
		if(index>-1){
			let p = [...this.filelist]
			p.splice(index,1,item)
			this.filelist = [...p];
		}
	}
	/**
	 * 成功后返回选择后的图片列表。
	 */
	async chooesefile():Promise<Array<file>>{
		let t = this;
		
		return new Promise(async (rs,rj)=>{
			let isready = await t.beforeChooesefile();
			if(!isready){
				rs([])
				return;
			}
			uni.chooseImage({
				count:t.config.maxFile,
				sourceType:t.config.fileType,
				fail: (e) => {
					rj("取消选择");
				},
				success: async (res) => {
					
					if(res.tempFilePaths.length==0){
						rj("未选择")
						return;
					}
					
					let imgarray = res.tempFilePaths;
					let fielist= res.tempFiles;
					let jgsk:Array<file> = [];
					//0待上传，1上传中，2上传失败，3上传成功。4超过大小限制
					imgarray.forEach((item:string,index:number)=>{
						let isMaxsize = fielist[index].size>t.config.maxSize?true:false;
						jgsk.push({
							url:item,
							status:isMaxsize?'超过大小':"待上传",
							progress:isMaxsize?100:0,
							uid:getUid(),
							statusCode:isMaxsize?statusCode.max:statusCode.upload,
							response:null,
							name:fielist[index].name??""
						})
					})

					let isreadyChoose = await t.chooesefileAfter(jgsk);
					
					if(!Array.isArray(isreadyChoose)||typeof isreadyChoose !='object'){
						rj("chooesefileAfter:函数过滤，没有返回文件列表。")
						return;
					}
					
					t.filelist.push(...isreadyChoose)
					
					t.chooesefileSuccess(isreadyChoose);
					rs(isreadyChoose)
					if(t.config.autoUpload){
						setTimeout(function() {
							t.start();
						}, 500);
					}
					
					
				}
			})
		})
	}
	async chooseMPH5weixinFile(){
		let t = this;
		return new Promise((rs,rj)=>{
			var fs = uni.chooseFile;
			// #ifdef MP-WEIXIN || MP-QQ
			fs = uni.chooseMessageFile;
			// #endif
			var config = {
				count:t.config.maxfile,
				type:t.config.type,
				extension:t.config.extension,
			}
			if(!t.config.extension||!Array.isArray(t.config.extension)||t.config.extension?.length==0){
				delete config.extension
			}
			fs({
				...config,
				fail: (e) => {
					console.error(e);
					uni.$tm.toast("已取消选择");
					rj(e);
				},
				success: (res) => {
					if(res.tempFiles.length==0){
						uni.$tm.toast("未选择");
						return;
					}
					let fielist = res.tempFiles;
					let jgsk = [];
					//0待上传，1上传中，2上传失败，3上传成功。4超过大小限制
					fielist.forEach((item,index)=>{
						let isMaxsize = fielist[index].size>t.config.maxsize?true:false;
						let ftype = item.name||""
						if(ftype){
							ftype = ftype.substr(ftype.lastIndexOf(".")+1).toLocaleLowerCase();
						}
						jgsk.push({
							url:item.path,
							name:item.name||'默认文件名称',
							type:ftype,
							status:isMaxsize?'超过大小':"待上传",
							progress:isMaxsize?100:0,
							fileId:guid(),
							statusCode:isMaxsize?4:0,
							data:null,//上传成功后的回调数据。
						})
					})
					t.filelist.push(...jgsk)
					
					t.selected(t.filelist);
					if(t.config.isAuto){
						t.start();
					}
					
					rs(t.filelist)
				}
			})
			
		})
	}
	setConfig(config:fileConfig){
		this.config={...this.config,...config??{}}
	}
	/**
	 * 动态加入文件
	 * @param {Object} filelist
	 */
	addFile(filelist:Array<file> = []){
		if(typeof filelist !=='object'&&!Array.isArray(filelist)) return;
		let total_uid = new Set(this.filelist.map(e=>e.uid))
		let total_url = new Set(this.filelist.map(e=>e.url))
		let cfilelist = filelist.map(el=>{
			return {...el,
				status:el?.status??"待上传",
				statusCode:el?.statusCode??statusCode.upload,
				uid:el?.uid??getUid(),
				progress:el?.progress??0,
				name:el?.name??"",
				response:el?.response??null,
				url:el?.url??""
			}
		})
		let filterFIle = cfilelist.filter(item=>!total_uid.has(item.uid)&&!total_url.has(item.url))
		this.filelist.push(...filterFIle)

	}
	beforeSuccess(item:file){
		return Promise.resolve(true);
	}
	beforeStart(item:file){
		return Promise.resolve(true);
	}
	// 进度。
	progress(item:file){}
	// 失败
	fail(item:file){}
	// 成功
	success(item:file,fileList:Array<file>){}
	// 完成。
	complete (filelist:file){}
	uploadComplete (filelist:Array<file>){}
	awaitTime(){
		return new Promise((resolve)=>{
			setTimeout(()=>{
				resolve(true)
			},20)
		})
	}
	// 开始上传。
	async start(){
		
		if(this.filelist.length<=0){
			console.error("未选择图片,已取消上传")
			return;
		}
		let t = this;
		// t重新开始上传从头开始。
		this.index = 0;
		this.isStop = false;
		async function startupload(){
			if(t.isStop) return;
			
			let item = t.filelist[t.index];
			
			if(!item || typeof item === 'undefined'){
				// 文件不存在。直接结束。
				t.uploadComplete(t.filelist);
				return;
			}
			
			let canbleStart =  await t.beforeStart(item)
			if(!canbleStart){
				item.statusCode = statusCode.fail;
				item.status = "不允许上传"
				t.filelist.splice(t.index,1,item)
				t.index++;
				t.setFileStatus(item)
				t.fail(item)
				t.complete(item);
				startupload();
				return;
			}
			
			if(item.statusCode==3||item.statusCode==1||item.statusCode==4||item.statusCode==2){
				// 直接跳过。至下一个文件。
				t.index++;
				startupload();
				return;
			}
			
			item.statusCode = statusCode.uploading;
			item.status = "上传中..."
			t.setFileStatus(item)
			const upObj = t.uploadobj = uni.uploadFile({
				url:String(t.config.hostUrl),
				name:t.config?.formName??'file',
				header:t.config?.header??{},
				filePath:item.url,
				formData:{name:item.name,...t.config.formData},
				success:async (res)=>{
					if(t.isStop) return
					item.response = res.data;
					let isOksuccess = await t.beforeSuccess(item);
					if(res.statusCode !=200||!isOksuccess){
						item.statusCode = statusCode.fail;
						item.status = "上传失败";
						t.fail(item)
						t.setFileStatus(item)
						t.index++;
						return;
					}
					
					// 上传成功。
					item.statusCode = statusCode.success;
					item.status = "上传成功";
					t.setFileStatus(item)
					t.success(item,t.filelist)
					t.index++;
				},
				fail:(res)=>{
					if(t.isStop) return
					item.statusCode = statusCode.fail;
					item.status = "上传失败";
					t.setFileStatus(item)
					t.fail(item)
					t.index++;
				},
				complete:async (res)=>{
					if(t.isStop) return
					await t.awaitTime();
					t.complete(item);
					// 直接下一个文件。
					startupload();
				}
			})
			if(upObj){
				let item = t.filelist[t.index];
				upObj.onProgressUpdate(async (res)=>{
					if(t.isStop) return
					item.progress = res.progress;
					item.statusCode = statusCode.uploading;
					item.status = "上传中...";
					t.setFileStatus(item)
					t.progress(item)
					
				})
			}
			
		}
		await startupload();
	}
	// 停止上传
	stop(){
		this.isStop = true;
		if(this.uploadobj!=null){
			this.uploadobj.abort()
		}
	}
	
}
